#include <QtGui>

#include "BaseParser.h"
#include "GlobalOrderLoader.h"
#include "LocalOrderLoader.h"
#include "ComplexOrderLoader.h"

enum ordertype {
    LOCALORDER = 0,
    GLOBALORDER,
    COMPLEXORDER
};

const QString ordername[] = {
    "LOCALORDER",
    "GLOBALORDER",
    "COMPLEXORDER"
};

static const std::string def_filename = "../doc/shaks200/hamlet.xml";
static const std::string def_username = "root";
static const std::string def_passwd = "123456";
static const int def_capacity = 17000;
static const int def_order = LOCALORDER;

struct args_t {
    QString filename;
    QString username;
    QString passwd;
    int capacity;
    int order;

    args_t()
	: filename(def_filename.c_str())
	, username(def_username.c_str())
	, passwd(def_passwd.c_str())
	, capacity(def_capacity)
	, order(def_order) {}
};

args_t processArgs(int argc, char **argv)
{
    args_t result;
    for (int i = 1; i < argc; ++i) {
	if (QString(argv[i]) == "-f") {
	    result.filename = QString(argv[++i]);
	} else if (QString(argv[i]) == "-u") {
	    result.username = QString(argv[++i]);
	} else if (QString(argv[i]) == "-p") {	
	    result.passwd = QString(argv[++i]);    
	} else if (QString(argv[i]) == "-c") {
	    result.capacity = atoi(argv[++i]);
	} else if (QString(argv[i]) == "-o") {
	    QString order = QString(argv[++i]);
	    if (order == QString("local")) {
		result.order = LOCALORDER;
	    } else if (order == QString("global")) {
		result.order = GLOBALORDER;
	    } else if (order == QString("complex")) {
		result.order = COMPLEXORDER;
	    } else {
                cout << "Unrecognized ordering scheme: " << order.toStdString() << endl;
	    }
	} else {
            cout << "Unrecognized command line option: " << argv[i] << endl;
	}
    }
    return result;
}

uint callProcedure(const QString& proc, int order)
{
    struct timespec startTime,
        endTime;

    QSqlQuery query = QSqlQuery(QSqlDatabase::database());

    if (!query.exec("use db" + ordername[order].toLower())) {
        cout << query.lastError().text().toStdString() << endl;
        return -1;
    }

    clock_gettime(CLOCK_MONOTONIC, &startTime);
    if (!query.exec("CALL " + proc)) {
        cout << "Nao foi possivel executar corretamente o procedimento " << proc.toStdString() << endl;
        cout << query.lastError().text().toStdString() << endl;
        return -1;
    }
    clock_gettime(CLOCK_MONOTONIC, &endTime);
    return (uint) timespecToMilisecond(endTime) - timespecToMilisecond(startTime);
}

#define MANYTIMES   10

void execManyTimes(const QString& proc, int order)
{
    uint ac = 0;
    for (int i = 0; i < MANYTIMES + 1; ++i) {
        if (proc == "deleteFirstAct")
            callProcedure("copyLastAct", order);

        uint tmp = callProcedure(proc, order);
        if (i != 0)
            ac += tmp;

        if (proc == "copyLastAct")
            callProcedure("deleteFirstAct", order);
    }
    cout << "CALL " << proc.toStdString() << " : " << ac / MANYTIMES << " ms" << endl;
}

int main(int argc, char *argv[])
{
    args_t args = processArgs(argc, argv);

    cout << "file " << args.filename.toStdString() << endl;
    cout << "buffer_capacity " << args.capacity << endl;
    cout << "order " << ordername[args.order].toStdString() << endl;

    QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL");
    db.setHostName("localhost");
    db.setUserName(args.username);
    db.setPassword(args.passwd);
    db.setPort(3306);
    if (!db.open()) {
        cout << "Nao foi possivel abrir o bd" << endl;
        throw -1;
    }

    uint ac = 0;
    for (int i = 0; i < MANYTIMES + 1; ++i) {
        BaseLoader* loader;
        switch(args.order) {
            case LOCALORDER: loader = new LocalOrderLoader(args.username, args.passwd, args.capacity); break;
            case GLOBALORDER: loader = new GlobalOrderLoader(args.username, args.passwd, args.capacity); break;
            case COMPLEXORDER: loader = new ComplexOrderLoader(args.username, args.passwd, args.filename, args.capacity); break;
            default: return -1;
        }

        BaseParser handler(args.filename, *loader);

        if (i != 0)
            ac += handler.getLoaderTime();
        cout << "Tomada " << i << ": " << handler.getLoaderTime() << " ms" << endl;

        delete loader;
    }
    cout << "LOAD: " << ac / MANYTIMES << " ms" << endl;

    QVector<QString> tasks;
    tasks.push_back("copyLastAct");
    tasks.push_back("deleteFirstAct");
    tasks.push_back("_play");
    tasks.push_back("_play_act_scene_speech");
    tasks.push_back("_play_act__speech");
    tasks.push_back("_play_act_scene_speech_speaker_bernardo");
    tasks.push_back("_play_act_title_act_i_scene_speech_speaker_bernardo");
    tasks.push_back("_play_act_scene_speech_2");
    
    for (QVector<QString>::iterator it = tasks.begin(); it != tasks.end(); ++it)
        execManyTimes(*it, args.order);

    return 0;
}
